package com.bh.common.utils.uuid;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDate;

/**
 * @Author: lzp
 * @description: 短id工具类
 * 注意： 使用此工具类，需要自行配置定时任务每日删除该缓存key，具体策略看生成方法的注解
 * @Date: 2022/8/15
 */
@Component
@Slf4j
public class ShortIdUtils {

    /**
     * 短id 缓存key
     */
    public static final String SHORT_ID_SET_KEY = "short_id_set";

    /**
     * 最大重试次数
     */
    public static final int MAX_RETRY_COUNT = 10;

    /**
     * 最大重试记录
     */
    public static int maxCount = 0;
    /**
     * 碰撞次数
     */
    public static int collisionCount = 0;

    @Resource(name = "redisTemplate")
    private SetOperations<String, Object> setOperations;

    /**
     * 生成规则 年份后两位【2位】 + 1年内第几天【3位】 + 随机数【5位】
     * 理论上来说，一百年内不会重复
     * 注意：1.前五位保证每天生成的id不重复，后五位会在redis缓存 set集合中进行碰撞校验，
     * 2.请保证每天0时清除缓存，保证缓存中的短id数量较少提高性能，减少内存浪费
     */
    public Long generate() {
        Long id = null;
        int count = 0;
        boolean exists = true;
        // 如果redis中已有该id，重试生成短id
        while (exists) {
        		 // 一下两个仅用于测试碰撞情况，生产环境请注释掉，并且只适用于单线程测试
            if (count > collisionCount) {
                collisionCount++;
            }
            if (count > maxCount) {
                maxCount = count;
            }
            
            id = this.generateShortId();
            exists = setOperations.add(SHORT_ID_SET_KEY, id.toString()) == 0;
            count++;
            if (count >= MAX_RETRY_COUNT) {
                log.error("尝试生成短id发生碰撞超过10次！！");
                return null;
            }
        }
        return id;
    }

    /**
     * 生成短id
     */
    private Long generateShortId() {
        LocalDate now = LocalDate.now();
        String yearStr = now.getYear() + "";
        yearStr = yearStr.substring(2);
        String dayOfYearStr = now.getDayOfYear() + "";
        if (dayOfYearStr.length() < 3) {
            StringBuilder zeroStr = new StringBuilder();
            for (int i = dayOfYearStr.length(); i < 3; i++) {
                zeroStr.append("0");
            }
            dayOfYearStr = zeroStr + dayOfYearStr;
        }
        StringBuilder id = new StringBuilder(yearStr + dayOfYearStr);
        for (int i = id.length(); i < 10; i++) {
            id.append(getRandomIndex(0, 9));
        }
        return Long.parseLong(id.toString());
    }


    /**
     * 返回范围内的随机int
     *
     * @param min 最小值
     * @param max 最大值
     * @return
     */
    private int getRandomIndex(int min, int max) {
        return min + (int) (Math.random() * (max - min + 1));
    }

}

